﻿/**
*    Copyright (C) 2008  Chase Kernan 
*    chase.kernan@gmail.com
*
*    This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
* 
*/

package com.chasekernan.hxnova.core.dataholders;

import com.chasekernan.hxnova.utils.Vector;

/**
    Holds a set of particles that can be accessed by id (using reflection).
    NOTE: If T does not inherit from SpaceObject, or it doesn't have the fields
    [location] and [id] this will not work properly.
**/
class ParticleHolder<T> {
    
    
    public var particles : Array<T>;
    
    public function new() {
        particles = new Array();
    }
    
    /**
        Returns a particle by the given id.
        Will throw an error if the id is out of range.
        NOTE: it will return null if the particle by that id no longer exists.
    **/
    public function getById(id : Int) : T {
        if (id < 0 || id >= particles.length) 
            throw "The given id is out of range.";
            
        return particles[id];
    }
    
    /**
        Appends a particle to the end of the array and adjusts the the id 
        accordingly.
    **/
    public function appendParticle(p : T) {
        Reflect.setField(p, "id", particles.length);
        particles.push(p);
    }
    
    /**
        Adds the particle to the array by the id the particle currently has.
        If the [id] is greater than the length of the array, then the array is 
        expanded to meet that id.
        Will throw an error if the [id] < 0, the [id] == null, or if the 
        [particle] == null.
    **/
    public function addParticle(p : T) {
        if (p == null) throw "The particle cannot be null.";
        
        #if flash9
        var id = cast(Reflect.field(p, "id"), Null<Int>);
        if (id == null) throw "The id cannot be null.";
        if (id < 0) throw "The id cannot be less than 0.";
        #else
        var id = Reflect.field(p, "id");
        if (untyped id == null) throw "The id cannot be null.";
        if (untyped id < 0) throw "The id cannot be less than 0.";
        #end
        
        if (id >= particles.length) {
            for (i in 0...(id - particles.length + 1)) particles.push(null);
        }
        
        particles[id] = p;
    }
    
    /**
        Removes the given particle from the array be setting its element to 
        null.
        NOTE: This will do nothing if the [particle] == null or the 
        [id] == null, but it will throw an error if the [id] < 0.
    **/
    public function removeParticle(p : T) {
        if (p == null) return;
        
        #if flash9
        var id = cast(Reflect.field(p, "id"), Null<Int>);
        if (id == null) throw "The id cannot be null.";
        if (id < 0) throw "The id cannot be less than 0.";
        #else
        var id = Reflect.field(p, "id");
        if (untyped id == null) throw "The id cannot be null.";
        if (untyped id < 0) throw "The id cannot be less than 0.";
        #end
        
        particles[id] = null;
    }
    
    /**
        Expands the array to the length given by filling the extra elements 
        with nulls.
        Will throw an error if [to] is less than the current length.
    **/
    public function expand(to : Int) {
        for (i in 0...(to - particles.length + 1)) particles.push(null);    
    }
    
    public function getParticlesAtLocation(location : Vector) : Array < T > {
        var pa = new Array<T>();
        
        for (particle in particles) {
            if (location.equals(untyped Reflect.field(particle, "location"))) {
                pa.push(particle);
            }
        }
        
        return pa;
    }
    
    public function iterator() {
        return particles.iterator();
    }
    
    /**
        Copies the structure holding the particles, but not the data itself.
    **/
    public function clone() : ParticleHolder<T> {
        var ph = new ParticleHolder<T>();
        
        ph.particles = particles.copy();
        
        return ph;
    }
    
}